using Docker.DotNet;
using Docker.DotNet.Models;
using Microsoft.Extensions.Logging;
using System.Formats.Tar;

namespace SumerCoreDevOps.Infrastructure.Libraries;

/// <summary>
/// Docker 操作库
/// </summary>
public class DockerLibrary
{
    private readonly ILogger<DockerLibrary> _logger;
    private readonly DockerClient _dockerClient;

    /// <summary>
    /// 日志事件（消息，级别）
    /// </summary>
    public event Action<string, string>? OnLog;

    public DockerLibrary(ILogger<DockerLibrary> logger, string dockerUri = "unix:///var/run/docker.sock")
    {
        _logger = logger;
        _dockerClient = new DockerClientConfiguration(new Uri(dockerUri)).CreateClient();
    }

    /// <summary>
    /// 记录日志并触发事件
    /// </summary>
    private void Log(string message, string level = "Info")
    {
        switch (level)
        {
            case "Info":
                _logger.LogInformation(message);
                break;
            case "Warning":
                _logger.LogWarning(message);
                break;
            case "Error":
                _logger.LogError(message);
                break;
            default:
                _logger.LogDebug(message);
                break;
        }

        OnLog?.Invoke(message, level);
    }

    /// <summary>
    /// 构建镜像
    /// </summary>
    public async Task<bool> BuildImageAsync(
        string dockerfilePath,
        string imageName,
        string tag = "latest",
        Dictionary<string, string>? buildArgs = null,
        CancellationToken cancellationToken = default)
    {
        try
        {
            Log($"开始构建镜像: {imageName}:{tag}");

            // Docker.DotNet requires a tar stream - create tar archive from directory
            var directoryPath = Path.GetDirectoryName(dockerfilePath)!;
            var tarPath = Path.Combine(Path.GetTempPath(), $"docker-build-{Guid.NewGuid()}.tar");

            // Create tar archive (simplified - in production use a proper tar library)
            await CreateTarArchiveAsync(directoryPath, tarPath);

            using var fileStream = File.OpenRead(tarPath);

            var parameters = new ImageBuildParameters
            {
                Tags = new List<string> { $"{imageName}:{tag}" },
                Dockerfile = "Dockerfile",
                BuildArgs = buildArgs ?? new Dictionary<string, string>()
            };

            // 使用新的 BuildImageFromDockerfileAsync API，支持进度报告和认证
            var progress = new Progress<JSONMessage>(message =>
            {
                if (!string.IsNullOrEmpty(message.Stream))
                {
                    Log($"Docker build: {message.Stream.TrimEnd()}", "Debug");
                }
                if (!string.IsNullOrEmpty(message.ErrorMessage))
                {
                    Log($"Docker build error: {message.ErrorMessage}", "Error");
                }
            });

            await _dockerClient.Images.BuildImageFromDockerfileAsync(
                contents: fileStream,
                parameters: parameters,
                authConfigs: null, // 无需认证配置（可选）
                headers: null,     // 无需自定义头（可选）
                progress: progress,
                cancellationToken: cancellationToken);

            // Clean up temp tar file
            if (File.Exists(tarPath))
            {
                File.Delete(tarPath);
            }

            Log($"镜像构建成功: {imageName}:{tag}");
            return true;
        }
        catch (Exception ex)
        {
            Log($"构建镜像失败: {imageName}:{tag} - {ex.Message}", "Error");
            return false;
        }
    }

    /// <summary>
    /// 创建 Tar 归档文件
    /// </summary>
    private async Task CreateTarArchiveAsync(string sourceDirectory, string tarPath)
    {
        try
        {
            _logger.LogDebug("创建 Tar 归档: {Source} -> {Tar}", sourceDirectory, tarPath);

            // 使用 .NET 内置的 System.Formats.Tar API
            await using var tarStream = File.Create(tarPath);
            await using var tarWriter = new TarWriter(tarStream);

            // 递归添加目录中的所有文件
            await AddDirectoryToTarAsync(tarWriter, sourceDirectory, sourceDirectory);

            _logger.LogDebug("Tar 归档创建成功: {Tar}", tarPath);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "创建 Tar 归档失败: {Source}", sourceDirectory);
            throw;
        }
    }

    /// <summary>
    /// 递归添加目录到 Tar 归档
    /// </summary>
    private async Task AddDirectoryToTarAsync(TarWriter tarWriter, string currentPath, string basePath)
    {
        // 添加当前目录中的所有文件
        foreach (var filePath in Directory.GetFiles(currentPath))
        {
            var relativePath = Path.GetRelativePath(basePath, filePath);

            // 使用 Unix 风格的路径分隔符（Docker 要求）
            var entryName = relativePath.Replace('\\', '/');

            _logger.LogDebug("添加文件到 Tar: {File}", entryName);

            // 创建 Tar 条目
            var entry = new PaxTarEntry(TarEntryType.RegularFile, entryName)
            {
                ModificationTime = File.GetLastWriteTimeUtc(filePath),
                Mode = UnixFileMode.UserRead | UnixFileMode.UserWrite |
                       UnixFileMode.GroupRead | UnixFileMode.OtherRead
            };

            // 写入文件内容
            await using var fileStream = File.OpenRead(filePath);
            entry.DataStream = fileStream;
            await tarWriter.WriteEntryAsync(entry);
        }

        // 递归处理子目录
        foreach (var directoryPath in Directory.GetDirectories(currentPath))
        {
            var dirName = Path.GetFileName(directoryPath);

            // 跳过特殊目录
            if (dirName == ".git" || dirName == "bin" || dirName == "obj" ||
                dirName == "node_modules" || dirName == ".vs")
            {
                continue;
            }

            await AddDirectoryToTarAsync(tarWriter, directoryPath, basePath);
        }
    }

    /// <summary>
    /// 推送镜像
    /// </summary>
    public async Task<bool> PushImageAsync(
        string imageName,
        string tag,
        string? username = null,
        string? password = null)
    {
        try
        {
            Log($"开始推送镜像: {imageName}:{tag}");

            var authConfig = new AuthConfig();
            if (!string.IsNullOrEmpty(username) && !string.IsNullOrEmpty(password))
            {
                authConfig.Username = username;
                authConfig.Password = password;
            }

            var parameters = new ImagePushParameters
            {
                ImageID = $"{imageName}:{tag}",
                Tag = tag
            };

            await _dockerClient.Images.PushImageAsync(
                imageName,
                parameters,
                authConfig,
                new Progress<JSONMessage>());

            Log($"镜像推送成功: {imageName}:{tag}");
            return true;
        }
        catch (Exception ex)
        {
            Log($"推送镜像失败: {imageName}:{tag} - {ex.Message}", "Error");
            return false;
        }
    }

    /// <summary>
    /// 运行容器
    /// </summary>
    public async Task<string?> RunContainerAsync(
        string imageName,
        string tag,
        string? containerName = null,
        Dictionary<string, string>? environmentVars = null,
        Dictionary<string, string>? portBindings = null)
    {
        try
        {
            _logger.LogInformation("开始运行容器: {Image}:{Tag}", imageName, tag);

            var createParams = new CreateContainerParameters
            {
                Image = $"{imageName}:{tag}",
                Name = containerName,
                Env = environmentVars?.Select(kv => $"{kv.Key}={kv.Value}").ToList(),
                HostConfig = new HostConfig
                {
                    PortBindings = portBindings?.ToDictionary(
                        kv => kv.Key,
                        kv => (IList<PortBinding>)new List<PortBinding>
                        {
                            new PortBinding { HostPort = kv.Value }
                        })
                }
            };

            var response = await _dockerClient.Containers.CreateContainerAsync(createParams);

            await _dockerClient.Containers.StartContainerAsync(
                response.ID,
                new ContainerStartParameters());

            _logger.LogInformation("容器启动成功: {ContainerId}", response.ID);
            return response.ID;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "运行容器失败: {Image}:{Tag}", imageName, tag);
            return null;
        }
    }

    /// <summary>
    /// 停止并移除容器
    /// </summary>
    public async Task<bool> StopAndRemoveContainerAsync(string containerId)
    {
        try
        {
            _logger.LogInformation("停止容器: {ContainerId}", containerId);

            await _dockerClient.Containers.StopContainerAsync(
                containerId,
                new ContainerStopParameters { WaitBeforeKillSeconds = 10 });

            await _dockerClient.Containers.RemoveContainerAsync(
                containerId,
                new ContainerRemoveParameters { Force = true });

            _logger.LogInformation("容器已停止并移除: {ContainerId}", containerId);
            return true;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "停止容器失败: {ContainerId}", containerId);
            return false;
        }
    }

    /// <summary>
    /// 获取容器日志
    /// </summary>
    public async Task<string> GetContainerLogsAsync(string containerId, int tail = 100)
    {
        try
        {
            var parameters = new ContainerLogsParameters
            {
                ShowStdout = true,
                ShowStderr = true,
                Tail = tail.ToString()
            };

            var stream = await _dockerClient.Containers.GetContainerLogsAsync(
                containerId,
                false,
                parameters);

            // MultiplexedStream needs special handling
            using var memoryStream = new MemoryStream();
            await stream.CopyOutputToAsync(Stream.Null, memoryStream, Stream.Null, CancellationToken.None);
            memoryStream.Position = 0;
            using var reader = new StreamReader(memoryStream);
            return await reader.ReadToEndAsync();
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "获取容器日志失败: {ContainerId}", containerId);
            return string.Empty;
        }
    }
}
